/*
	Copyright (C) 2009 Salil Bhagurkar
	
	This file is part of ViSE
	
	ViSE is free software: you can redistribute it and/or modify
	it under the terms of the GNU Lesser General Public License as published by
	the Free Software Foundation, either version 3 of the License, or
	(at your option) any later version.
	
	ViSE is distributed in the hope that it will be useful,
	but WITHOUT ANY WARRANTY; without even the implied warranty of
	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
	GNU Lesser General Public License for more details.
	
	You should have received a copy of the GNU Lesser General Public License
	along with ViSE.  If not, see <http://www.gnu.org/licenses/>.
*/


/*Expression evaluator*/


#include <shared.h>

#define PTYPE_CONSTANT 0
#define PTYPE_SYMBOL 1
#define PTYPE_OPERATION 2
#define PTYPE_ACCUMULATOR 3 //Space to store result of previous operation. Store the incrementing location in constant

struct pobject {
	u8 type;
	union {
		u32 constant;
		char symbol[FS_NAME_LEN];
		u32 operation; //No need for 32 bits here. But we won't be saving space otherwise anyway.
	};
	struct pobject *next, *prev;
};

static void print_pobject(struct pobject *t)
{
	switch(t->type) {
		case PTYPE_CONSTANT:
			printk("%u ", t->constant);
			return;
		case PTYPE_SYMBOL:
			printk("%s ", t->symbol);
			return;
		case PTYPE_OPERATION:
			printk("%c ", t->operation);
			return;
		case PTYPE_ACCUMULATOR:
			printk("result#%u ", t->constant);
			return;
	}
}


static struct pobject *pobjects = NULL, *pobjectstail = NULL;



struct pobject *new_accumulator(u32 pos)
{
	struct pobject *n;
	n = (struct pobject *)malloc(sizeof(struct pobject));
	n->type = PTYPE_ACCUMULATOR;
	n->constant = pos;
	return n;
}


#define POBJECT_STACK_SZ 20

//pobject stack

struct pobject_stack {
	u32 pobject_stack_ptr;
	struct pobject *pobject_stack[POBJECT_STACK_SZ];
};

static struct pobject_stack stack = {.pobject_stack_ptr = 0}, opstack = {.pobject_stack_ptr = 0};

static void push_pobject(struct pobject_stack *stack, struct pobject *p)
{
	if((stack->pobject_stack_ptr) < POBJECT_STACK_SZ)
		stack->pobject_stack[(stack->pobject_stack_ptr)++] = p;
	else
		printk("pobject stack full\n");
	/*printk("pushed:");
	print_pobject(p);
	printk("\n");*/
}


static struct pobject *pop_pobject(struct pobject_stack *stack)
{
	struct pobject *ret;
	if((stack->pobject_stack_ptr) > 0) {
		ret = stack->pobject_stack[--(stack->pobject_stack_ptr)];
		/*printk("popped:");
		print_pobject(ret);
		printk("\n");*/
		return ret;
	}
	printk(".");
	return NULL;
}

static struct pobject *stack_base(struct pobject_stack *stack)
{
	return stack->pobject_stack[stack->pobject_stack_ptr - 1];
}


static void new_constant(u32 constant)
{
	struct pobject *n;
	n = (struct pobject *)malloc(sizeof(struct pobject));
	n->type = PTYPE_CONSTANT;
	n->constant = constant;
	list_attach(pobjects, pobjectstail, n);
}

static void new_symbol(char *symbol)
{
	struct pobject *n;
	n = (struct pobject *)malloc(sizeof(struct pobject));
	n->type = PTYPE_SYMBOL;
	strcpy(n->symbol, symbol);
	list_attach(pobjects, pobjectstail, n);
}

static void new_operation(u32 operation)
{
	struct pobject *n;
	n = (struct pobject *)malloc(sizeof(struct pobject));
	n->type = PTYPE_OPERATION;
	n->operation = operation;
	list_attach(pobjects, pobjectstail, n);
}


static int is_symbol_character(char x)
{
	return ((x>='0'&&x<='9')||(x>='a'&&x<='z')||(x>='A'&&x<='Z')||(x=='_'));
}

static int is_operation(char x)
{
	return ((x=='+')||(x=='-')||(x=='*')||(x=='/')||(x=='(')||(x==')'));
}


static int is_white(char x)
{
	return ((x==' ')||(x=='\t'));
}


int precedence(u32 temp)
{
	if(temp=='(' || temp==')')
		return 3;
	if(temp=='^')
		return 2;
	if(temp=='*' || temp=='/' || temp=='%')
		return 1;
	else
		return 0;
}


//Temporary linked list of instructions generated from the infix parsing
struct instruction {
	u16 flags; //Specifies what is to be done, and the nature of arguments
	union {
		u32 op0;
		struct node *node0;
		u32 alloc0; //Temporary space
	};
	union {
		u32 op1;
		struct node *node1;
		u32 alloc1; //Temporary space
	};
	struct instruction *next, *prev;
};


static struct instruction *stream = NULL, *streamtail = NULL;
static u32 stream_size = 0;


void print_stream(struct instruction *head)
{
	struct instruction *p;
	list_for(head, p) {
		printk("<%3x>", p->flags);
		if((p->flags & PARAM0_MASK) == (INSTR_CONSTANT << 6))
			printk(" %u ", p->op0);
		else if((p->flags & PARAM0_MASK) == (INSTR_SYMBOL << 6))
			printk(" %s ", p->node0->name);
		else if((p->flags & PARAM0_MASK) == (INSTR_ALLOC << 6))
			printk(" a#%u ", p->alloc0);
		if((p->flags & PARAM1_MASK) == (INSTR_CONSTANT << 8))
			printk(" %u ", p->op1);
		else if((p->flags & PARAM1_MASK) == (INSTR_SYMBOL << 8))
			printk(" %s ", p->node1->name);
		else if((p->flags & PARAM1_MASK) == (INSTR_ALLOC << 8))
			printk(" a#%u ", p->alloc1);
		printk("\n");
	}
}


int add_instruction(struct pobject *op, struct pobject *p0, struct pobject *p1)
{
	struct instruction *n;
	/*print_pobject(op);
	print_pobject(p0);
	print_pobject(p1);
	printk("\n");*/
	n = (struct instruction *)malloc(sizeof(struct instruction));
	switch(op->operation) {
		case '+': n->flags = OP_ADD;
		break;
		case '-': n->flags = OP_SUB;
		break;
		case '*': n->flags = OP_MUL;
		break;
		case '/': n->flags = OP_DIV;
		break;
	}
	if(p0->type == PTYPE_CONSTANT) {
		n->op0 = p0->constant;
		n->flags |= (INSTR_CONSTANT << 6);
	} else if(p0->type == PTYPE_SYMBOL) {
		n->node0 = app_find_node(p0->symbol);
		if(!(n->node0)) {
			printk("instruction: symbol node %s not found\n", p0->symbol);
			return ENOREG;
		}
		n->flags |= (INSTR_SYMBOL << 6);
	} else if(p0->type == PTYPE_ACCUMULATOR) {
		n->alloc0 = p0->constant;
		n->flags |= (INSTR_ALLOC << 6);
	}
	if(p1->type == PTYPE_CONSTANT) {
		n->op1 = p1->constant;
		n->flags |= (INSTR_CONSTANT << 8);
	} else if(p1->type == PTYPE_SYMBOL) {
		n->node1 = app_find_node(p1->symbol);
		if(!(n->node1)) {
			printk("instruction: symbol node %s not found\n", p1->symbol);
			return ENOREG;
		}
		n->flags |= (INSTR_SYMBOL << 8);
	} else if(p1->type == PTYPE_ACCUMULATOR) {
		n->alloc1 = p1->constant;
		n->flags |= (INSTR_ALLOC << 8);
	}
	list_attach(stream, streamtail, n);
	stream_size++;
	return 0;
}


struct mathvm *make_stream(u32 nr_clients)
{
	struct mathvm *mathvm;
	struct node_stream *node_stream;
	struct instruction *t;
	u32 i;
	t = stream;
	node_stream = (struct node_stream *)malloc(sizeof(struct node_stream) * stream_size);
	for(i = 0; i < stream_size; i++) {
		memcpy(&node_stream[i], t, sizeof(struct node_stream));
		t = t->next;
	}
	mathvm = (struct mathvm *)malloc(sizeof(struct mathvm) + nr_clients * sizeof(struct node *));
	mathvm->stream = node_stream;
	mathvm->count = stream_size;
	
	return mathvm;
}


#define NUM_LEN 20

struct mathvm *parse_expression(const char *exp, u32 nr_clients)
{
	u32 i, j, slen;
	struct pobject *t;
	char temp[NUM_LEN];
	char sym[FS_NAME_LEN];
	char revi=0;
	struct instruction *instr;
	slen = strlen(exp);
	printk("Parse:%s\n", exp);
	stream_size = 0;
	for(i=0; i<slen; i++) {
		if(exp[i] >= '0' && exp[i] <= '9') { //Scan a constant
			if(exp[i] == '0' && exp[i+1] == 'x') { //Hex number
				j = 0;
				temp[j++] = exp[i++]; //Get the '0x' first
				temp[j++] = exp[i++];
				revi = 0;
				while((exp[i] >= '0' && exp[i] <= '9') || (exp[i] >= 'a' && exp[i] <= 'f') || (exp[i] >= 'A' && exp[i] <= 'F')) {
					if(j > (NUM_LEN-2))
						break;
					temp[j++] = exp[i++]; //Put the whole number
					revi = 1;
				}
				temp[j] = 0;
				new_constant(getnumber(temp));
				if(revi) i--; //Cut the extra increment
			} else {
				j = 0;
				revi = 0;
				while((exp[i] >= '0' && exp[i] <= '9')) {
					if(j > (NUM_LEN-2))
						break;
					temp[j++] = exp[i++]; //Put the whole number
					revi = 1;
				}
				temp[j] = 0;
				new_constant(getnumber(temp));
				if(revi) i--;
			}
		} else {
			//Check if it is an operation
			if(is_operation(exp[i])) {
				new_operation((u32)exp[i]);
			} else if(is_symbol_character(exp[i]) && !(is_white(exp[i]))) {
				j = 0;
				revi = 0;
				while(is_symbol_character(exp[i]) && !(is_white(exp[i]))) {
					if(j > (NUM_LEN-2))
						break;
					sym[j++] = exp[i++]; //Put the whole number
					revi = 1;
				}
				sym[j] = 0;
				new_symbol(sym);
				if(revi) i--;
			}
		}
	}
	//Now do the linear parsing.
	u32 pos = 0;
	list_for(pobjects, t) {
		if(t->type == PTYPE_CONSTANT || t->type == PTYPE_SYMBOL) {
			push_pobject(&stack, t);
		} else {
			if(t->operation == ')') {
				while((opstack.pobject_stack_ptr) > 0) {
					struct pobject *op, *p0, *p1;
					op = pop_pobject(&opstack);
					if(op->operation == '(')
						break;
					p0 = pop_pobject(&stack);
					p1 = pop_pobject(&stack);
					if(add_instruction(op, p1, p0)) {
						goto noway;
					}
					push_pobject(&stack, new_accumulator(pos++));
				}
			} else {
				if(precedence(t->operation) > precedence(stack_base(&opstack)->operation) || opstack.pobject_stack_ptr==0 || stack_base(&opstack)->operation == '(') {
					push_pobject(&opstack, t);
				} else {
					struct pobject *op, *p0, *p1;
					op = pop_pobject(&opstack);
					p0 = pop_pobject(&stack);
					p1 = pop_pobject(&stack);
					if(add_instruction(op, p1, p0)) {
						goto noway;
					}
					push_pobject(&stack, new_accumulator(pos++));
					push_pobject(&opstack, t);
				}
			}
		}
	}
	while(opstack.pobject_stack_ptr > 0) {
		struct pobject *op, *p0, *p1;
		op = pop_pobject(&opstack);
		p0 = pop_pobject(&stack);
		p1 = pop_pobject(&stack);
		if(add_instruction(op, p1, p0))
			goto noway;
		push_pobject(&stack, new_accumulator(pos++));
	}
	
	
	//Now create a node with the stream
	
	struct mathvm *ret;
	
	ret = make_stream(nr_clients);
	
	
	list_destroy(pobjects, pobjectstail);
	list_destroy(stream, streamtail);
	
	return ret;
	
	
	
	noway:
	
	printk("Parse failed\n");
	
	return NULL;
}
